home *** CD-ROM | disk | FTP | other *** search
/ Shareware Grab Bag / Shareware Grab Bag.iso / 050 / madtrb1.arc / EXECUTE.INC < prev    next >
Text File  |  1986-02-27  |  18KB  |  390 lines

  1. { EXECUTE.INC }
  2.  
  3. { *************************************************************************** }
  4. { *                                                                         * }
  5. { *                TURBO SCREEN INPUT PRE-PROCESSOR TOOLKIT                 * }
  6. { *                                                                         * }
  7. { *                    EXECUTE SUBPROGRAM INCLUDE FILE                      * }
  8. { *                                                                         * }
  9. { *                             Version  1.07                               * }
  10. { *                                                                         * }
  11. { *                                                                         * }
  12. { *    This include file was written with routines supplied by Bela Lubkin. * }
  13. { *    He has asked that his routines be for non-commercial use only EXCEPT * }
  14. { *    with permission from him.                                            * }
  15. { *                                                                         * }
  16. { *    This include file contains routines that allow you to run other      * }
  17. { *    programs directly from Turbo Pascal, get the return codes when       * }
  18. { *    those programs terminate, and be able to temporarily exit your       * }
  19. { *    Turbo program to issue DOS commands.                                 * }
  20. { *                                                                         * }
  21. { *    All of these routines return to your next line of Pascal code once   * }
  22. { *    they have completed.                                                 * }
  23. { *                                                                         * }
  24. { *    You could, for example, use these routines within this include file  * }
  25. { *    to execute a child program, perhaps written in Fortran.  The user    * }
  26. { *    would be totally unaware that control had been passed to a different * }
  27. { *    program.  Once the child program terminates, control would then be   * }
  28. { *    passed back to the parent program, specifically to the next line of  * }
  29. { *    Pascal Code.  Thus one could write a Turbo Pascal shell program to   * }
  30. { *    execute other programs, thereby preventing the user from having to   * }
  31. { *    deal with DOS.  Please see the documentation file 'Tsipp.Doc' for    * }
  32. { *    more discussion on this interesting topic.                           * }
  33. { *                                                                         * }
  34. { *    VERY IMPORTANT NOTES                                                 * }
  35. { *                                                                         * }
  36. { *    The Exec calls (SubProcess, SubProcessViaCOMMAND, ShellToDOS,        * }
  37. { *    ExecuteChildProgram) will not work unless you restrict Turbo's       * }
  38. { *    allocated heap.  To do this, lower "mAximum dynamic free memory"     * }
  39. { *    on the compiler Options menu to a reasonable value.  What a          * }
  40. { *    reasonable value is depends on your program's use of the heap and    * }
  41. { *    the stack, and must be determined by you.  If you use neither the    * }
  42. { *    heap (pointers) nor recursion, as low as 0400h (16K bytes) is        * }
  43. { *    probably more than enough.                                           * }
  44. { *                                                                         * }
  45. { *    The Exec calls CANNOT be called from within the interactive Turbo    * }
  46. { *    compiler system.  They can only be called from .COM or .CHN files    * }
  47. { *    running outside of the Turbo environment.                            * }
  48. { *                                                                         * }
  49. { *    Revision history                                                     * }
  50. { *    ----------------                                                     * }
  51. { *    Version 1.5 1/14/86 fixes the memory freeing bug by removing         * }
  52. { *                support for Turbo 2.0.  String types changed to          * }
  53. { *                minimize chances of collision.  General environment      * }
  54. { *                support added.  Explicit calls for Exec-via-COMMAND.COM  * }
  55. { *                and Exec-to-DOS-prompt added.  Support for getting the   * }
  56. { *                subprocess return code added.  Major documentation       * }
  57. { *                overhaul.  NOW REQUIRES TURBO 3.0!  (Thanks to Stu       * }
  58. { *                Fuller 76703,501 for pointing out how easy it was to add * }
  59. { *                full environment support).                               * }
  60. { *    Version 1.4 attempts to fix a bug in the freeing of memory before    * }
  61. { *                the Exec call.                                           * }
  62. { *    Version 1.3 works with MS-DOS 2.0 and up, TURBO PASCAL version 1.0   * }
  63. { *                and up.                                                  * }
  64. { *    Version 1.2 had a subtle but dangerous bug: I set a variable that    * }
  65. { *                was addressed relative to BP, using a destroyed BP!      * }
  66. { *    Version 1.1 didn't work with Turbo 2.0 because I used Turbo 3.0      * }
  67. { *                features.                                                * }
  68. { *    Version 1.0 only worked with DOS 3.0 due to a subtle bug in DOS 2.x  * }
  69. { *                                                                         * }
  70. { *    -  Bela Lubkin                                                       * }
  71. { *       CompuServe 76703,3015                                             * }
  72. { *                                                                         * }
  73. { *************************************************************************** }
  74.  
  75. Type
  76.   ExecuteStr66=String[66];
  77.   ExecuteStr255=String[255];
  78.  
  79.  
  80.  
  81. Function SubProcess(    CommandLine:ExecuteStr255):Integer;
  82.  
  83. { Calls an executable image (.COM or .EXE file) using MS-DOS function
  84.   4Bh, Exec.  The parameter CommandLine must contain both the name of the
  85.   program to run and the arguments to be passed to it, seperated by a
  86.   space.  Path searching and other amenities are not performed; the passed
  87.   in name must be specific enough to allow the file to be found, i.e.
  88.   'CHKDSK' will NOT work.  At least 'CHKDSK.COM' must be specified, and a
  89.   drive and path name will help even more.  For example,
  90.  
  91.         'C:\SYSTEM\CHKDSK.COM'
  92.         'A:\WS.COM DOCUMENT.1'
  93.         'C:\DOS\LINK.EXE TEST;'
  94.         'D:\ASSEM\MASM.EXE PROG1 PROG1.OBJ NUL PROG1.MAP'
  95.         'C:\COMMAND.COM /C COPY *.* B:\BACKUP >FILESCOP.IED'
  96.  
  97.   The last example uses COMMAND.COM to invoke a DOS internal command and
  98.   to perform redirection.  Only with the use of COMMAND.COM can the
  99.   following be done: redirection; piping; path searching; searching for
  100.   the extension of a program (.COM, .EXE, or .BAT); batch files; and
  101.   internal DOS commands.
  102.  
  103.   Because the COMMAND-assisted Exec function is so useful, a seperate
  104.   function, SubProcessViaCOMMAND, is provided for that purpose.
  105.   The integer return value of SubProcess is the error value returned by
  106.   DOS on completion of the Exec call.  If it is nonzero, the call failed.
  107.   Here is a list of likely error values:
  108.  
  109.          0: Success
  110.          2: File/path not found
  111.          3: Path not found
  112.          4: Too many files open (no handles left)
  113.          5: Access denied
  114.          8: Not enough memory to load program
  115.         10: Illegal environment (greater than 32K)
  116.         11: Illegal .EXE file format
  117.         32: Sharing violation
  118.         33: Lock violation
  119.  
  120.   If you get any other result, consult an MS-DOS Technical Reference
  121.   manual. }
  122.  
  123. Const
  124.   STK_SEG: Integer=0;                  { structured constant to store stack segment }
  125.   STK_PTR: Integer=0;                  { structured constant to store stack pointer }
  126.  
  127. Var
  128.   Register:
  129.     Record
  130.       Case Integer Of { Variant Record Type }
  131.         1 : (AX,BX,CX,DX,BP,SI,DI,DS,ES,Flags: Integer); { store 16 bit register values }
  132.         2 : (AL,AH,BL,BH,CL,CH,DL,DH: Byte);             { store 8 bit register values }
  133.       End; { Register Record }
  134.   FileControlBlock1: Array [0..36] Of Byte;
  135.   FileControlBlock2: Array [0..36] Of Byte;
  136.   PathName: ExecuteStr66;
  137.   CommandTail: ExecuteStr255;
  138.   ParmTable:
  139.     Record
  140.       EnvSeg: Integer;
  141.       ComLin: ^Integer;
  142.       FileControlBlock1Ptr: ^Integer;
  143.       FileControlBlock2Ptr: ^Integer;
  144.     End; { ParmTable Record }
  145.   RegisterFlags: Integer;
  146.  
  147. Begin   { SubProcess }
  148.   If Pos(' ',CommandLine)=0 Then
  149.     Begin
  150.       PathName:=CommandLine+#0;
  151.       CommandTail:=^M;
  152.     End { If Pos }
  153.   Else
  154.     Begin
  155.       PathName:=Copy(CommandLine,1,Pred(Pos(' ',CommandLine)))+#0;
  156.       CommandTail:=Copy(CommandLine,Pos(' ',CommandLine),255)+^M;
  157.     End; { Else }
  158.   CommandTail[0]:=Pred(CommandTail[0]);
  159.   With Register Do
  160.     Begin
  161.       FillChar(FileControlBlock1,Sizeof(FileControlBlock1),0);
  162.       AX:=$2901;
  163.       DS:=Seg(CommandTail[1]);
  164.       SI:=Ofs(CommandTail[1]);
  165.       ES:=Seg(FileControlBlock1);
  166.       DI:=Ofs(FileControlBlock1);
  167.       MsDos(Register); { Create FileControlBlock 1 }
  168.       FillChar(FileControlBlock2,Sizeof(FileControlBlock2),0);
  169.       AX:=$2901;
  170.       ES:=Seg(FileControlBlock2);
  171.       DI:=Ofs(FileControlBlock2);
  172.       MsDos(Register); { Create FileControlBlock 2 }
  173.       With ParmTable Do
  174.         Begin
  175.           EnvSeg:=MemW[CSeg:$002C];
  176.           ComLin:=Addr(CommandTail);
  177.           FileControlBlock1Ptr:=Addr(FileControlBlock1);
  178.           FileControlBlock2Ptr:=Addr(FileControlBlock2);
  179.         End; { With ParmTable }
  180.       InLine($8D/$96/ PathName /$42/        { <DX>:=Ofs(PathName[1]); }
  181.              $8D/$9E/ ParmTable /           { <BX>:=Ofs(ParmTable);   }
  182.              $B8/$00/$4B/                   { <AX>:=$4B00;            }
  183.              $1E/$55/                       { Save <DS>, <BP>         }
  184.              $16/$1F/                       { <DS>:=Seg(PathName[1]); }
  185.              $16/$07/                       { <ES>:=Seg(ParmTable);   }
  186.              $2E/$8C/$16/ STK_SEG /         { Save <SS> in STK_SEG     }
  187.              $2E/$89/$26/ STK_PTR /         { Save <SP> in STK_PTR     }
  188.              $FA/                           { Disable interrupts      }
  189.              $CD/$21/                       { Call MS-DOS             }
  190.              $FA/                           { Disable interrupts      }
  191.              $2E/$8B/$26/ STK_PTR /         { Restore <SP>            }
  192.              $2E/$8E/$16/ STK_SEG /         { Restore <SS>            }
  193.              $FB/                           { Enable interrupts       }
  194.              $5D/$1F/                       { Restore <BP>,<DS>       }
  195.              $9C/$8F/$86/ RegisterFlags /   { Flags:=<CPU flags>      }
  196.              $89/$86/ Register );           { Register.AX:=<AX>;      }
  197.       { The messing around with SS and SP is necessary because under DOS 2.x,
  198.         after returning from an EXEC call, ALL registers are destroyed except
  199.         CS and IP!  I wish I'd known that before I released this package the
  200.         first time... }
  201.       If (RegisterFlags And 1)<>0 Then
  202.         SubProcess:=AX
  203.       Else
  204.         SubProcess:=0;
  205.     End; { With Register }
  206. End;    { SubProcess }
  207.  
  208.  
  209.  
  210. Function GetEnvStr(    SearchString: ExecuteStr255): ExecuteStr255;
  211.  
  212. { Gets a string from the MS-DOS environment.  The parameter SearchString
  213.   specifies the desired environment string.  The function result returns
  214.   the value of that string from the environment.  If the string is not
  215.   found, a null string is returned.  SearchString may have one special
  216.   value, '='.  This returns garbage under MS-DOS 2.x.  Under MS-DOS 3.x,
  217.   it returns the pathname under which the currently running program was
  218.   invoked.  Examples:
  219.  
  220.         GetEnvStr('COMSPEC')   might = 'C:\COMMAND.COM'
  221.         GetEnvStr('PROMPT')    might = '$p $g'
  222.         GetEnvStr('REFLEX')    might = 'Herc'
  223.         GetEnvStr('=')         might = 'C:\TURBO\exectest.COM'
  224.  
  225.   Only an exact match will succeed; case IS significant.  Do not include
  226.   an equal sign in the search string (GetEnvStr('COMSPEC=') will fail).
  227.   Note: if you are wondering why there is no SetEnvStr procedure, read
  228.   an MS-DOS Technical Reference manual. }
  229.  
  230. Type
  231.   Env=Array [0..32767] Of Char;
  232.  
  233. Var
  234.   EPtr: ^Env;
  235.   EStr: ExecuteStr255;
  236.   Done: Boolean;
  237.   I: Integer;
  238.  
  239. Begin   { GetEnvStr }
  240.   GetEnvStr:='';
  241.   If SearchString<>'' Then
  242.     Begin
  243.       EPtr:=Ptr(MemW[CSeg:$002C],0);
  244.       I:=0;
  245.       SearchString:=SearchString+'=';
  246.       Done:=False;
  247.       EStr:='';
  248.       Repeat
  249.         If EPtr^[I]=#0 Then
  250.           Begin
  251.             If EPtr^[Succ(I)]=#0 Then
  252.               Begin
  253.                 Done:=True;
  254.                 If SearchString='==' Then
  255.                   Begin
  256.                     EStr:='';
  257.                     I:=I+4;
  258.                     While EPtr^[I]<>#0 Do
  259.                       Begin
  260.                         EStr:=EStr+EPtr^[I];
  261.                         I:=Succ(I);
  262.                       End; { While EPtr^ }
  263.                     GetEnvStr:=EStr;
  264.                   End; { If SearchString }
  265.               End; { If EPtr^ }
  266.             If Copy(EStr,1,Length(SearchString))=SearchString Then
  267.               Begin
  268.                 GetEnvStr:=Copy(EStr,Succ(Length(SearchString)),255);
  269.                 Done:=True;
  270.               End; { If Copy }
  271.             EStr:='';
  272.           End { If EPtr^ }
  273.         Else
  274.           EStr:=EStr+EPtr^[I];
  275.         I:=Succ(I);
  276.       Until Done;
  277.     End; { If SearchString }
  278. End;    { GetEnvStr }
  279.  
  280.  
  281.  
  282. Function GetComSpec: ExecuteStr66;
  283.  
  284. { This is a special case of GetEnvStr and simply returns the COMSPEC
  285.   environment string.  It is included for compatability with previous
  286.   EXEC.PAS versions. }
  287.  
  288. Begin   { GetComSpec }
  289.   GetComSpec:=GetEnvStr('COMSPEC');
  290. End;    { GetComSpec }
  291.  
  292.  
  293.  
  294. Function SubProcessViaCOMMAND(    CommandLine: ExecuteStr255): Integer;
  295.  
  296. { This is a special case of SubProcess.  The CommandLine is passed to
  297.   COMMAND.COM, which does all further processing.  Command lines invoked
  298.   via this procedure can do redirection and piping; undergo the normal DOS
  299.   PATH search; may be batch files; and may be internal DOS commands such
  300.   as COPY and RENAME.
  301.  
  302.   Disadvantages of this approach are: a copy of COMMAND.COM must be
  303.   present (not always true on a floppy-based system); a slight time and
  304.   memory penalty is involved due to the loading of an extra copy of
  305.   COMMAND.COM (about 3K under DOS 3.1); the subprocess return code
  306.   (Errorlevel) is lost.  In most cases the benefits will outweight the
  307.   disadvantages.
  308.  
  309.   The integer return code is the same as for SubProcess. }
  310.  
  311. Begin   { SubProcessViaCOMMAND }
  312.   SubProcessViaCOMMAND:=SubProcess(GetComSpec+' /C '+CommandLine);
  313. End;    { SubProcessViaCOMMAND }
  314.  
  315.  
  316.  
  317. Function SubProcessReturnCode: Integer;
  318.  
  319. { This function calls MS-DOS function 4Dh, Get Return Code of a
  320.   Sub-process.  The integer return value is the return code set by the
  321.   last subprocess you called.  Like Turbo's IOResult, SubProcessReturnCode
  322.   is only valid once after a SubProcess call, reverting to 0 on successive
  323.   calls.  The return code obtained after using SubProcessViaCOMMAND is the code
  324.   returned by COMMAND.COM, not by any other program, and is not likely to be
  325.   useful.
  326.  
  327.   Note: Turbo programs can set the return code by using the Halt
  328.   procedure with a parameter, e.g. Halt(20);.  Other languages can call
  329.   DOS function 4Ch (Terminate) with the return code in AL. }
  330.  
  331. Var
  332.   Register:
  333.     Record
  334.       Case Integer Of { Variant Record Type }
  335.         1 : (AX,BX,CX,DX,BP,SI,DI,DS,ES,Flags: Integer); { store 16 bit register values }
  336.         2 : (AL,AH,BL,BH,CL,CH,DL,DH: Byte);             { store 8 bit register values }
  337.       End; { Register Record }
  338.  
  339. Begin   { SubProcessReturnCode }
  340.   Register.AH:=$4D;
  341.   MsDos(Register);
  342.   SubProcessReturnCode:=Register.AX;
  343. End;    { SubProcessReturnCode }
  344.  
  345.  
  346.  
  347. Procedure ShellToDOS;
  348.  
  349. { This procedure allows you to temporarily exit your Turbo program and execute
  350.   DOS commands, and then re-enter your Turbo program where you left off
  351.   simply by typing 'exit'. }
  352.  
  353. Var
  354.   Command:ExecuteStr255;               { string used for issuing DOS commands }
  355.   CurrentDirectory:ExecuteStr66;       { string used to store the current logged directory }
  356.   ErrorCode:Integer;                   { variable used to determine error code from SubProcess function }
  357.   Exit:Boolean;                        { flag used in determining when to re-enter the user's Turbo program }
  358.  
  359. Begin   { ShellToDOS }
  360.   WriteLn('To re-enter "place program name here" type "exit"');
  361.   Exit:=False;                         { initialize flag }
  362.   Repeat
  363.     Command:='';                       { initialize to prevent duplicate commands to be issued by single CR }
  364.     GetDir(0,CurrentDirectory);        { get the current logged directory }
  365.     Write(CurrentDirectory,'=->');     { display turbo DOS prompt }
  366.     ReadLn(Command);                   { read user's DOS command }
  367.     If (Command='EXIT') Or (Command='Exit') Or (Command='exit') Then
  368.       Exit:=True                       { user has requested to return to Turbo program }
  369.     Else
  370.       If Command<>'' Then              { check for empty entry (CR) }
  371.         ErrorCode:=SubProcessViaCOMMAND(Command);
  372.   Until Exit;
  373. End;    { ShellToDOS }
  374.  
  375.  
  376.  
  377. Procedure ExecuteChildProgram(    ProgramNameAndPassedParameters:ExecuteStr255);
  378.  
  379. { This procedure allows you to call and run a child program directly from a
  380.   parent Turbo Pascal program, passing all required parameters to the child
  381.   program to be executed.  Control is then returned to the next line of Pascal
  382.   code in the parent program once the child program has terminated. }
  383.  
  384. Var
  385.   ErrorCode:Integer;                   { variable used to determine error code from SubProcess function }
  386.  
  387. Begin   { ExecuteChildProgram }
  388.   ErrorCode:=SubProcess(ProgramNameAndPassedParameters);
  389. End;    { ExecuteChildProgram }
  390.